#!/usr/bin/perl

use strict;
use AppConfig qw(:expand :argcount);

### System variables

# Base directory
my $base 	= "/etc/news/filter/";
my $baseconf    = "/etc/news/postfilter/";

# Modules that are loaded at startup
my @modules = ( 
		"modules/articleformat.pm",
		"modules/configuration.pm",
		"modules/spool.pm",
		"modules/style.pm",
		"modules/iodata.pm"
	      );

our $delayederror;  # if something goes wrong during initialization, this variable is set to 1 and filter_nnrpd rejects the subsequent
		    # article before executing any check and than closes the connection with the client

our $config;	    # Configuration array
our $access;	    # Data about users' right (access files)
our $article;	    # Data about each article;
my  $style;	    # Data about style rules
our %hdr;	    # Variable that is internally filled by nnrpd with all headers
our $user;	    # The readers.conf user that are sending the article
our $body;	    # Body of each article passed here by nnrpd
our @quickref;	    # List of error strings 
our @twoleveltlds;  # List of two level tlds
our %active;	    # Active contents

### Everything here is executed a single time when nnrpd loads postfilter

&log( "notice", "Starting postfilter...." );   # a generic debug log line

# Now it's the time to load all modules required by postfilter
&log( "debug", "Loading required internal perl modules" );
foreach ( @modules ) {
	my $file = "$base/$_";
        my $return;
        &log("debug", "Loading module: $file");
        unless ($return = do $file) {
        	&log( "crit", "Fatal Error: couldn't parse $file: $@" ) if $@;
                &log( "crit", "Fatal Error: couldn't do $file: $!" )    unless defined $return;
                &log( "crit", "Fatal Error: couldn't run $file" )       unless $return;
                $delayederror = 1; # see above
        }
}

# Now it's the time to load postfilter.conf (from modules/configuration.pl)
if ($delayederror != 1 ) {
	$config = &load_config("$baseconf/postfilter.conf", 1); 
	if ($delayederror == 1) {
		&log("crit", "Syntax error inside $baseconf/postfilter.conf");
	}
}

# Now it's the time to load a list of second level tlds
if ($delayederror != 1) {
	my $file = $config->two_level_tlds_file();
        $delayederror = &load_two_level_tlds($file);
}

# Now it's the time to load active file if style_check is enabled
if (($delayederror != 1) and ($config->style_check == 1)) {
	$delayederror = &load_active($config->active_file);
}


########
#
# filter_post(): Main routine that is executed every time that an user posts a message.
#                If an user sends more than one message, filter is loaded only one time
#                but filter_post() is executed for each message.
#
#######

sub filter_post()
{
# If some error happens during initalization, message must be rejected and connection must be closed
	if ($delayederror == 1 ) {
		&log("crit", "Message rejected and connection closed due an internal critical failure");
		return "CLOSE";
	}

	&log("notice", "Analyzing message $hdr{'Message-ID'}, user $user" );

# if 'posting_allowed' is set to 'off' inside postfilter.conf articles must be always rejected without checking

        if ($config->posting_allowed() == 0) {
                &log("notice", "Posting disabled by postfilter.conf, rejected without checking");
                return "Posting not allowed";
        }

# Now it's the time to dectect which configuration directory must be read.
# This must be done here since nnrpd fills $user only when filter_nnrpd is executed
# If an UserName is not generated and so if $user is empty, nouser.conf will be searched
# Three options:
#   1. <postfilter_etcdir>/user/
#   2. <postfilter_etcdir>/nouser/
#   3. <postfilter_etcdir>/default/

	my $etcdir = &get_etcdir($baseconf,$user);
	return "CLOSE" if ($etcdir eq undef); # Note: everything was already logged by get_etcdir, a reminder here is useless

# Here $article is built with all numerical data needed by postfilter (things like number of crossposted groups or size)
	&log("notice", "Stage 1: Building internal data for the current article");
	$article = &create_article_data();  # Fetch data from the current article (%hdr)	


#
# SPOOL CHECK
#

$delayederror = 0; # Debug: this should be removed

# If spool_check == on inside postfilter.conf, it's the time to check whether the current ip has got the right to post
# This check must be executed as soon as possible because it's the most frequent rejection reason.
	if ($config->spool_check == 1) {

# Step 1: read the user's permissions configuration file ($etcdir/access.conf)
        my $accessfh = "$etcdir/access.conf";
        $access = &load_config($accessfh, 2); # if somewhat goes wrong, abort
        return "CLOSE" if ($delayederror == 1);

# Step 2: read the spool file 
		my $usr = $article->user();	# Note: AppConfig objects can't be invoked inside a quoted string so
		my $cip = $article->clientip(); # "User $article->user()" is invalid
		
		&log("notice", "Stage 2: Checking user's rights: User $user, Address $cip");
		my $success = &read_access($config->spool_file, $config->spool_expire_time);
		return "CLOSE" if ($success == 1);   # Postfilter can't read access spool so i've to abort

# Step 3: execute user's rights check
		if ($success != 2) {		     # if spool file exists, as usual, i make checks
			$success = &check_access();

# Step 4: reject article if needed		
		if ( $success > 0 ) {	     # If exit value is greater than zero, article must only to be rejected
				my $sss = &write_access($config->spool_file, $success);
        			return "CLOSE" if ($sss == 1);
				return $quickref[$success];
			} elsif ($success < 0) {    # If exit code is smaller than zero, article was rejected and connection has to be closed
				my $sss = &write_access($config->spool_file, ($success*-1));  # Debug: exit value must be positive
        			return "CLOSE";
			}
		} else {			    # If spool file doesn't yet exist
			&log("notice", "User's rights check is impossibile since access file was not yet created.");
		}
	} else {
		&log("debug", "User's right check disabled through postfilter.conf, skipping check" );
	}

#
# STYLE CHECK
#

	if ($config->style_check() == 1) {
		&log("notice", "Stage 3: style rules");

# Stage 1: Reading configuration file
		my $stylefile = "$etcdir/style.conf"; 
		$style = &load_config($stylefile, 3); # if somewhat goes wrong, abort
        	return "CLOSE" if ($delayederror == 1); # if somewhat goes wrong, abort (&syserr() logs)

# Stage 2: Making checks
		my $success = &check_style($style);

# Stage 3: rejecting 
		if ($success != 0) {
			my $sss = &write_access($config->spool_file, $success);
                        return "CLOSE" if ($sss == 1);
                        return $quickref[$success];
		}


	}


################################################################################################################################

	my $success = &write_access($config->spool_file, 0);
	return "CLOSE" if ($success == 1);

	$delayederror = 0;
	return "";
}





########
#
# log(): Log data through nnrpd
#
#######

sub log()    # log( severity, string1, string2.... );
{
        my @strings = @_;
        my $severity = shift(@strings);
        foreach ( @strings )
        {
                INN::syslog( $severity, $_ );
        }
}



